package com.cfuture08.eweb4j.jdbc.sql;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import com.cfuture08.eweb4j.cache.ORMConfigBeanCache;
import com.cfuture08.eweb4j.orm.config.bean.ORMConfigBean;
import com.cfuture08.eweb4j.orm.config.bean.Property;
import com.cfuture08.util.ReflectUtil;
import com.cfuture08.util.StringUtil;

/**
 * 生成更新语句
 * 
 * @author cfuture.aw
 * @since v1.a.0
 */
public class UpdateSqlCreator<T> {
	private T[] ts;

	public UpdateSqlCreator(T... ts) {
		this.ts = ts;
	}

	public String update(String condition) {
		if (this.ts != null && this.ts.length > 0) {
			StringBuilder sb = new StringBuilder();
			for (T t : this.ts) {
				sb.append(this.makeSQL(t, condition));
			}
			return sb.toString();
		} else {
			return "";
		}
	}

	private String makeSQL(T t, String condition) {
		ORMConfigBean ormBean = ORMConfigBeanCache.get(t.getClass());
		String table = ormBean != null ? ormBean.getTable() : t.getClass()
				.getSimpleName();
		return String.format("UPDATE %s %s ;", table, condition);
	}

	public String[] update() throws Exception {
		String[] sqls = new String[ts.length];
		for (int i = 0; i < ts.length; ++i) {
			sqls[i] = makeSQL(ts[i]);
		}

		return sqls;
	}

	public String[] update(String[] columns) throws Exception {
		String[] sqls = new String[ts.length];
		for (int i = 0; i < ts.length; ++i) {
			sqls[i] = makeSQL(ts[i], columns);
		}

		return sqls;
	}

	public String[] update(String[] columns, String[] values) throws Exception {
		String[] sqls = new String[ts.length];
		for (int i = 0; i < ts.length; ++i) {
			sqls[i] = makeSQL(ts[i], columns, values);
		}

		return sqls;
	}

	private String makeSQL(T t) throws Exception {
		ORMConfigBean ormBean = ORMConfigBeanCache.get(t.getClass());
		String table = ormBean == null ? t.getClass().getSimpleName() : ormBean
				.getTable();
		StringBuilder condition = new StringBuilder();
		StringBuilder values = new StringBuilder();
		ReflectUtil ru = new ReflectUtil(t);
		if (ormBean == null) {
			for (Field f : ru.getFields()) {
				if ("id".equals(f.getName())) {
					// 已经有主键存在，则组合。
					if (condition.length() > 0) {
						condition.append(" AND ");
					}

					condition.append(f.getName() + " = ");

					Method m = ru.getMethod("get"
							+ StringUtil.toUpCaseFirst(f.getName()));
					if (m != null) {
						Object value = m.invoke(t);
						if (Boolean.class.isAssignableFrom(f.getClass())) {
							value = "true".equals(value) ? "1" : "0";
						}
						condition.append("'" + value + "'");
					}

				} else {
					if (values.length() > 0)
						values.append(",");
					values.append(f.getName() + " = ");
					Method m = ru.getMethod("get"
							+ StringUtil.toUpCaseFirst(f.getName()));
					if (m != null) {
						Object value = m.invoke(t);
						if (Boolean.class.isAssignableFrom(f.getClass())) {
							value = "true".equals(value) ? "1" : "0";
						}

						values.append("'" + value + "'");
					}
				}
			}
		} else {
			for (Property property : ormBean.getProperty()) {
				if ("true".equals(property.getPk())
						|| "1".equals(property.getPk())) {
					// 已经有主键存在，则组合。
					if (condition.length() > 0) {
						condition.append(" AND ");
					}

					condition.append(property.getColumn() + " = ");

					Method m = ru.getMethod("get"
							+ StringUtil.toUpCaseFirst(property.getName()));
					if (m != null) {
						Object value = m.invoke(t);
						if ("boolean".equalsIgnoreCase(property.getType())) {
							value = "true".equals(value) ? "1" : "0";
						} else if (boolean.class.isAssignableFrom(m
								.getReturnType())) {
							value = "true".equals(value) ? "1" : "0";
						}
						condition.append("'" + value + "'");
					}

				} else {
					if (values.length() > 0)
						values.append(",");
					values.append(property.getColumn() + " = ");
					Method m = ru.getMethod("get"
							+ StringUtil.toUpCaseFirst(property.getName()));
					if (m != null) {
						Object value = m.invoke(t);
						if ("boolean".equalsIgnoreCase(property.getType())) {
							value = "true".equals(value) ? "1" : "0";
						} else if (boolean.class.isAssignableFrom(m
								.getReturnType())) {
							value = "true".equals(value) ? "1" : "0";
						}

						values.append("'" + value + "'");
					}
				}
			}
		}

		return String.format("UPDATE %s SET %s WHERE %s ;", table, values,
				condition);
	}

	private String makeSQL(T t, String[] columns) throws Exception {
		ORMConfigBean ormBean = ORMConfigBeanCache.get(t.getClass());
		String table = ormBean == null ? t.getClass().getSimpleName() : ormBean
				.getTable();
		StringBuilder condition = new StringBuilder();
		StringBuilder values = new StringBuilder();
		ReflectUtil ru = new ReflectUtil(t);
		if (ormBean == null) {
			for (Field f : ru.getFields()) {
				if ("id".equals(f.getName())) {
					if (condition.length() > 0) {
						condition.append(" AND ");
					}
					Object value = ru.getMethod(
							"get" + StringUtil.toUpCaseFirst(f.getName()))
							.invoke(t);
					condition.append(f.getName()).append(" = '");
					condition.append(value).append("'");
				}
				for (String column : columns) {
					if (column.equals(f.getName())) {
						Object value = ru.getMethod(
								"get" + StringUtil.toUpCaseFirst(f.getName()))
								.invoke(t);
						if (values.length() > 0) {
							values.append(", ");
						}

						values.append(column).append(" = '");
						values.append(value).append("'");

					}
				}
			}
		} else {
			for (Property property : ormBean.getProperty()) {
				if ("1".equals(property.getPk())
						|| "true".equals(property.getPk())) {
					if (condition.length() > 0) {
						condition.append(" AND ");
					}
					Object value = ru.getMethod(
							"get"
									+ StringUtil.toUpCaseFirst(property
											.getName())).invoke(t);
					condition.append(property.getColumn()).append(" = '");
					condition.append(value).append("'");
				}
				for (String column : columns) {
					if (column.equals(property.getColumn())) {
						Object value = ru.getMethod(
								"get"
										+ StringUtil.toUpCaseFirst(property
												.getName())).invoke(t);
						if (values.length() > 0) {
							values.append(", ");
						}

						values.append(column).append(" = '");
						values.append(value).append("'");

					}
				}
			}
		}
		return String.format("UPDATE %s SET %s WHERE %s ;", table, values,
				condition);
	}

	private String makeSQL(T t, String[] columns, String[] values)
			throws Exception {
		ORMConfigBean ormBean = ORMConfigBeanCache.get(t.getClass());
		String table = ormBean == null ? t.getClass().getSimpleName() : ormBean
				.getTable();
		StringBuilder condition = new StringBuilder();
		StringBuilder sb = new StringBuilder();
		ReflectUtil ru = new ReflectUtil(t);
		if (ormBean == null) {
			for (Field f : ru.getFields()) {
				if ("id".equals(f.getName())) {
					if (condition.length() > 0) {
						condition.append(" AND ");
					}
					Object value = ru.getMethod(
							"get" + StringUtil.toUpCaseFirst(f.getName()))
							.invoke(t);
					condition.append(f.getName()).append(" = '");
					condition.append(value).append("'");
				}
			}
		} else {
			for (Property property : ormBean.getProperty()) {
				if ("1".equals(property.getPk())
						|| "true".equals(property.getPk())) {
					if (condition.length() > 0) {
						condition.append(" AND ");
					}
					Object value = ru.getMethod(
							"get"
									+ StringUtil.toUpCaseFirst(property
											.getName())).invoke(t);
					condition.append(property.getColumn()).append(" = '");
					condition.append(value).append("'");
				}
			}
		}

		for (int i = 0; i < columns.length; ++i) {
			if (ru.getField(columns[i]) != null) {
				if (sb.length() > 0) {
					sb.append(", ");
				}

				sb.append(columns[i]).append(" = '");
				sb.append(values[i]).append("'");
			}
		}

		return String.format("UPDATE %s SET %s WHERE %s ;", table, sb
				.toString(), condition);
	}

	public T[] getTs() {
		return ts;
	}

	public void setTs(T[] ts) {
		this.ts = ts;
	}
}
